package com.yanqu.road.server.manager.tomb;

import com.yanqu.road.logic.activity.tomb.config.entity.ActivityTombEvent;
import com.yanqu.road.pb.tomb.Tomb;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.tomb.business.TombCrossBusiness;
import com.yanqu.road.server.manager.tomb.entity.*;
import com.yanqu.road.server.manager.union.activitygroup.Cross2UnionActivityGroupMgr;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TombDbData {

    private static Logger logger = LogManager.getLogger(TombServiceMgr.class.getName());

    private TombActivity activity;


    public static TombDbData newInstance(TombActivity activity) {
        TombDbData tombDbData = new TombDbData();
        tombDbData.activity = activity;
        tombDbData.loadActivityData();
        return tombDbData;
    }


    public void loadActivityData() {
        List<DbTombServerData> dbTombServerDataList = TombCrossBusiness.getDbTombServerData(this.activity.getActivityInfo().getActivityId());
        for (DbTombServerData dbTombServerData : dbTombServerDataList) {
            this.addDbTombServerData(dbTombServerData);
            this.refreshUserAbilityRanking(dbTombServerData.getGroupId());
        }
    }

    private Map<Integer, DbActivityTombInfo> dbActivityTombInfoMap = new ConcurrentHashMap<>();

    private final Map<Long, DbTombUser> dbTombUserMap = new ConcurrentHashMap<>();


    private final Map<Integer, List<DbTombUser>> dbTombUserRanking = new ConcurrentHashMap<>();

    private final ReadWriteLock dbTombUserRWLock = new ReentrantReadWriteLock();

    private final Map<Integer, Map<Integer, Map<String, DbTombPosition>>> dbTombPositionMap = new ConcurrentHashMap<>();

    private final Map<Integer, Map<Integer, DbTombMaze>> dbTombMazeMap = new ConcurrentHashMap<>();


    private final Map<Integer, Map<Long, DbTombServerData>> dbTombServerDataMap = new ConcurrentHashMap<>();
    private final Map<String, DbTombUserPosition> dbTombUserPositionMap = new ConcurrentHashMap<>();


    private final Map<Integer, List<UserAbilityInfo>> userAbilityInfoRankingMap = new ConcurrentHashMap<>();

    private ReadWriteLock rankingRWLock = new ReentrantReadWriteLock();

    public void addDbTombUser(DbTombUser dbTombUser) {
        if(!this.dbTombUserRanking.containsKey(dbTombUser.getGroupId())){
            this.dbTombUserRanking.put(dbTombUser.getGroupId(), new ArrayList<>());
        }
        if (!this.dbTombUserMap.containsKey(dbTombUser.getUserId())) {
            this.dbTombUserRanking.get(dbTombUser.getGroupId()).add(dbTombUser);
        }
        this.dbTombUserMap.put(dbTombUser.getUserId(), dbTombUser);
        this.activity.getActivityUnion(dbTombUser.getUserInfo().getUnionUid()).addDbTombUser(dbTombUser);
        if (dbTombUser.getUserNianTuNum() > 0) {
            this.activity.getActivityUnion(dbTombUser.getUserInfo().getUnionUid()).updateNiantuRank(dbTombUser); //0分不上榜
        }
    }


    public DbTombUser getDbTombUser(long userId) {
        //玩家没有分组，不生产数据
        int groupId = Cross2UnionActivityGroupMgr.getGroupId(activity.getActivityInfo().getActivityId(), userId);
        if(groupId < 0){
            return null;
        }
        DbTombUser dbTombUser = this.dbTombUserMap.get(userId);
        if (dbTombUser != null) {
            if(dbTombUser.getGroupId() != groupId){
                dbTombUser.setGroupId(groupId);
            }
            return dbTombUser;
        }
        if (userId == 0) {
            return null;
        }
        dbTombUser = initDbTombUser(userId);
        return dbTombUser;
    }

    public synchronized DbTombUser initDbTombUser(long userId) {
        DbTombUser dbTombUser = this.dbTombUserMap.get(userId);
        if (dbTombUser != null) {
            return dbTombUser;
        }
        dbTombUser = DbTombUser.newInstance();
        dbTombUser.setNeedInsert(true);
        dbTombUser.setUserId(userId);
        dbTombUser.setActivityId(this.activity.getActivityInfo().getActivityId());
        dbTombUser.setFloor(1);
        dbTombUser.setEnergy(this.activity.getConfig().getConfig().getTOMB_ENERGY_LIMIT());
        dbTombUser.setEnergyUpdateTime(System.currentTimeMillis());
        dbTombUser.refreshUpdateTime();
        dbTombUser.setUserNianTuNum(0);
        dbTombUser.setGuideMark(0);
        int groupId = Cross2UnionActivityGroupMgr.getGroupId(activity.getActivityInfo().getActivityId(), userId);
        //设置商会unionUid
        String unionUid = Cross2UnionActivityGroupMgr.getUnionUid(dbTombUser.getActivityId(), userId);
        if(!StringUtils.isNullOrEmpty(unionUid)){
            dbTombUser.getUserInfo().setUnionUid(unionUid);
        }else {
            logger.info("初始皇陵玩家 {}，分组{}， 商会空", userId, groupId);
        }
        dbTombUser.setGroupId(groupId);
        dbTombUser.setAssistTimes(this.activity.getConfig().getConfig().getTOMB_INITIAL_ASSISTS());
        this.addDbTombUser(dbTombUser);
        Map<Integer, Long> tmpDollMap = new ConcurrentHashMap<>();
        for (int dollId : activity.getConfig().getConfig().getTOMB_ALL_DOLL_ID_LIST()) {
            tmpDollMap.put(dollId, 0L);
        }
        dbTombUser.setDollMap(tmpDollMap);
        dbTombUser.setDollId(activity.getConfig().getConfig().getTOMB_ALL_DOLL_ID_LIST().get(0));
        dbTombUser.setSwitchList(new ArrayList<>());
//        Tomb.TombUserTopPatronsResp.Builder response = Tomb.TombUserTopPatronsResp.newBuilder();

//        DbTombUser finalDbTombUser = dbTombUser;
//        MessageHelper.sendToGameServer(serverId, GameProtocol.S_TOMB_USER_TOP_PATRONS_CROSS_TO_GAME, this.activity.getActivityInfo().getActivityId(),
//                userId, null, response).onSuccess(syncResponse -> {
//            finalDbTombUser.addPartnerInfoList(response.getListList());
//            finalDbTombUser.setMaxHp(finalDbTombUser.getTotalAbility());
//            finalDbTombUser.setHp(finalDbTombUser.getTotalAbility());
//        }).onFailure(throwable -> {
//            logger.error(throwable.getMessage());
//        });
        return dbTombUser;
    }


    public Map<Long, DbTombUser> getDbTombUserMap() {
        return dbTombUserMap;
    }

    public List<DbTombUser> getDbTombUserByGroupId(int groupId) {
        List<DbTombUser> list = new ArrayList<>();
        for (Map.Entry<Long, DbTombUser> userEntry : dbTombUserMap.entrySet()) {
            if(userEntry.getValue().getGroupId() == groupId){
                list.add(userEntry.getValue());
            }
        }
        return list;
    }

    public DbTombPosition getDbTombPosition(int groupId, int floor, String positionKey) {
        Map<Integer, Map<String, DbTombPosition>> groupMap = this.dbTombPositionMap.get(groupId);
        if(groupMap == null){
            return null;
        }
        Map<String, DbTombPosition> stringDbTombPositionMap = groupMap.get(floor);
        if (stringDbTombPositionMap == null) {
            return null;
        }
        return stringDbTombPositionMap.get(String.format("%d_%s", floor, positionKey));
    }

    public void addDbTombPosition(DbTombPosition dbTombPosition) {
        if (!this.dbTombPositionMap.containsKey(dbTombPosition.getGroupId())) {
            this.dbTombPositionMap.put(dbTombPosition.getGroupId(), new ConcurrentHashMap<>());
        }
        if (!this.dbTombPositionMap.get(dbTombPosition.getGroupId()).containsKey(dbTombPosition.getFloor())) {
            this.dbTombPositionMap.get(dbTombPosition.getGroupId()).put(dbTombPosition.getFloor(), new ConcurrentHashMap<>());
        }
        this.dbTombPositionMap.get(dbTombPosition.getGroupId()).get(dbTombPosition.getFloor()).put(dbTombPosition.getKey(), dbTombPosition);
    }

    public DbTombPosition initDbTombPosition(int groupId, int floor, Position position) {
        DbTombPosition dbTombPosition = DbTombPosition.newInstance();
        dbTombPosition.setNeedInsert(true);
        dbTombPosition.setActivityId(this.activity.getActivityInfo().getActivityId());
        dbTombPosition.setFloor(floor);
        dbTombPosition.setX(position.getRow());
        dbTombPosition.setY(position.getColumn());
        dbTombPosition.setpEnum(position.getpEnum());
        dbTombPosition.seteType(position.geteType());
        dbTombPosition.setGroupId(groupId);
        this.addDbTombPosition(dbTombPosition);
        return dbTombPosition;
    }

    public Map<Integer, Map<Integer, Map<String, DbTombPosition>>> getDbTombPositionMap() {
        return dbTombPositionMap;
    }

    public Map<String, DbTombPosition> getDbTombPositionMap(int groupId, int floor){
        if(!dbTombPositionMap.containsKey(groupId)){
            return null;
        }
        return dbTombPositionMap.get(groupId).get(floor);
    }

    public Map<Integer, Map<Long, DbTombServerData>> getDbTombServerDataMap() {
        return dbTombServerDataMap;
    }

    public Map<Long, DbTombServerData> getDbTombServerDataMap(int groupId) {
        if(!dbTombServerDataMap.containsKey(groupId)){
            return new HashMap<>();
        }
        return dbTombServerDataMap.get(groupId);
    }

    public DbTombServerData getDbTombServerData(int activityId, long serverId, int groupId) {
        if(!this.dbTombServerDataMap.containsKey(groupId)){
            this.dbTombServerDataMap.put(groupId, new ConcurrentHashMap<>());
        }
        DbTombServerData dbTombServerData = this.dbTombServerDataMap.get(groupId).get(serverId);
        if (dbTombServerData != null) {
            return dbTombServerData;
        }
        dbTombServerData = DbTombServerData.newInstance();
        dbTombServerData.setActivityId(activityId);
        dbTombServerData.setServerId(serverId);
        dbTombServerData.setGroupId(groupId);
        dbTombServerData.setNeedInsert(true);
        this.dbTombServerDataMap.get(groupId).put(dbTombServerData.getServerId(), dbTombServerData);
        return dbTombServerData;
    }

    public void addDbTombServerData(DbTombServerData dbTombServerData) {
        int groupId = dbTombServerData.getGroupId();
        if(!this.dbTombServerDataMap.containsKey(groupId)){
            this.dbTombServerDataMap.put(groupId, new ConcurrentHashMap<>());
        }
        this.dbTombServerDataMap.get(groupId).put(dbTombServerData.getServerId(), dbTombServerData);

    }

    public void refreshUserAbilityRanking(int groupId) {
        this.rankingRWLock.writeLock().lock();
        try {
            this.getUserAbilityInfoRanking(groupId).clear();
            for (DbTombServerData dbTombServerData : this.dbTombServerDataMap.get(groupId).values()) {
                if(groupId == dbTombServerData.getGroupId()) {
                    this.getUserAbilityInfoRanking(groupId).addAll(dbTombServerData.getData());
                }
            }
            this.getUserAbilityInfoRanking(groupId).sort(Comparator.comparing(UserAbilityInfo::getAbility).reversed());
        } finally {
            this.rankingRWLock.writeLock().unlock();
        }

    }

    public void refreshActiveDbTombUserAbilityRanking() {
        this.dbTombUserRWLock.writeLock().lock();
        try {
            for (Map.Entry<Integer, List<DbTombUser>> entry : this.dbTombUserRanking.entrySet()) {
                entry.getValue().sort(Comparator.comparing(DbTombUser::getTotalAttack).reversed());
            }
            //this.dbTombUserRanking.sort(Comparator.comparing(DbTombUser::getTotalAttack).reversed());
        } finally {
            this.dbTombUserRWLock.writeLock().unlock();
        }
    }

    public List<DbTombUser> getDbTombUserRanking(int groupId) {
        this.dbTombUserRWLock.readLock().lock();
        if(!dbTombUserRanking.containsKey(groupId)){
            dbTombUserRanking.put(groupId, new ArrayList<>());
        }
        try {
            List<DbTombUser> list = new ArrayList<>(this.dbTombUserRanking.get(groupId));
            return list;
        } finally {
            this.dbTombUserRWLock.readLock().unlock();
        }
    }

    private List<UserAbilityInfo> getUserAbilityInfoRanking(int groupId){
        if(!this.userAbilityInfoRankingMap.containsKey(groupId)){
            this.userAbilityInfoRankingMap.put(groupId, new ArrayList<>());
        }
        return this.userAbilityInfoRankingMap.get(groupId);
    }
    
    private long _getHpByRank(int groupId, int start, int end) {
        long total = 0;
        long count = 0;
        for (int i = start; i <= end; i++) {
            if (i > this.getUserAbilityInfoRanking(groupId).size()) {
                break;
            }
            int index = i - 1;
            if (index < 0) {
                continue;
            }
            UserAbilityInfo userAbilityInfo = this.getUserAbilityInfoRanking(groupId).get(index);
            total = total + userAbilityInfo.getAbility();
            count++;
        }
        if (count == 0) {
            return 0;
        }
        return total / count;
    }

    public long getHpByRank(int groupId, int start, int end) {
        this.rankingRWLock.readLock().lock();
        try {
            int size = this.getUserAbilityInfoRanking(groupId).size();
            if (size == 0) {
                return 0;
            }
            return this._getHpByRank(groupId, start, end);
        } finally {
            this.rankingRWLock.readLock().unlock();
        }
    }

    public long getHpByRatio(int groupId, int start, int end) {
        this.rankingRWLock.readLock().lock();
        try {
            int size = this.getUserAbilityInfoRanking(groupId).size();
            if (size == 0) {
                return 0;
            }
            start = (size * start + 1000 - 1) / 1000;
            end = (size * end + 1000 - 1) / 1000;
            return this._getHpByRank(groupId, start, end);
        } finally {
            this.rankingRWLock.readLock().unlock();
        }

    }

    public void addDbTombUserPosition(DbTombUserPosition dbTombUserPosition) {
        String s = String.format("%d_%d_%d_%d_%d", dbTombUserPosition.getGroupId(), dbTombUserPosition.getFloor(), dbTombUserPosition.getUserId(), dbTombUserPosition.getX(), dbTombUserPosition.getY());
        this.dbTombUserPositionMap.put(s, dbTombUserPosition);
    }

    public DbTombUserPosition getDbTombUserPosition(int groupId, int floor, long usrId, int row, int column) {
        String s = String.format("%d_%d_%d_%d_%d", groupId, floor, usrId, row, column);
        return this.dbTombUserPositionMap.get(s);
    }

    public Map<String, DbTombUserPosition> getDbTombUserPositionMap() {
        return dbTombUserPositionMap;
    }

    public DbTombUserPosition initDbTombUserPosition(int groupId, int floor, long usrId, int row, int column) {
        DbTombUserPosition dbTombUserPosition = DbTombUserPosition.newInstance();
        dbTombUserPosition.setActivityId(this.activity.getActivityInfo().getActivityId());
        dbTombUserPosition.setGroupId(groupId);
        dbTombUserPosition.setUserId(usrId);
        dbTombUserPosition.setFloor(floor);
        dbTombUserPosition.setX(row);
        dbTombUserPosition.setY(column);
        dbTombUserPosition.setNeedInsert(true);
        this.addDbTombUserPosition(dbTombUserPosition);
        return dbTombUserPosition;
    }

    public int getUpgradeTimes(int floor, long userId) {
        int count = 0;
        for (DbTombUserPosition value : this.dbTombUserPositionMap.values()) {
            if (value.getFloor() == floor && value.getUserId() == userId) {
                count++;
            }
        }
        return count;
    }

    public DbActivityTombInfo getDbActivityTombInfo(int groupId) {
        if(dbActivityTombInfoMap.containsKey(groupId)){
            return dbActivityTombInfoMap.get(groupId);
        }
        DbActivityTombInfo dbActivityTombInfo;
        synchronized (dbActivityTombInfoMap){
            dbActivityTombInfo = dbActivityTombInfoMap.get(groupId);
            if(dbActivityTombInfo != null){
                return dbActivityTombInfo;
            }
            dbActivityTombInfo = this.initDbActivityTombInfo();
            dbActivityTombInfo.setGroupId(groupId);
            this.dbActivityTombInfoMap.put(groupId, dbActivityTombInfo);
        }
        return dbActivityTombInfo;
    }

    public Map<Integer, DbActivityTombInfo> getDbActivityTombInfoMap() {
        return dbActivityTombInfoMap;
    }

    public void setDbActivityTombInfoMap(Map<Integer, DbActivityTombInfo> dbActivityTombInfoMap) {
        this.dbActivityTombInfoMap = dbActivityTombInfoMap;
    }

    public DbActivityTombInfo initDbActivityTombInfo() {
        DbActivityTombInfo dbActivityTombInfo1 = DbActivityTombInfo.newInstance();
        dbActivityTombInfo1.setActivityId(this.activity.getActivityInfo().getActivityId());
        dbActivityTombInfo1.setMaxFloor(this.activity.getConfig().getInitMaxFloor());
        dbActivityTombInfo1.setBossFloor(this.activity.getConfig().getInitMaxFloor());
        dbActivityTombInfo1.refreshUpdateTime();
        dbActivityTombInfo1.setNeedInsert(true);
        return dbActivityTombInfo1;
    }


    public int generateTotalActivityNum(int activityId) {
        int activityNum = 0;
        for (Map.Entry<Integer, Map<Long, DbTombServerData>> mapEntry : dbTombServerDataMap.entrySet()) {
            for (DbTombServerData dbTombServerData : mapEntry.getValue().values()) {
                if (dbTombServerData.getActivityId() != activityId) {
                    continue;
                }
                activityNum = activityNum + dbTombServerData.getActivityUserNum();
            }
        }

        return activityNum;
    }


    public DbTombMaze getDbTombMaze(int groupId, int floor) {
        if(!this.dbTombMazeMap.containsKey(groupId)){
            return null;
        }
        return this.dbTombMazeMap.get(groupId).get(floor);
    }

    public void addDbTombMaze(DbTombMaze dbTombMaze) {
        if(!this.dbTombMazeMap.containsKey(dbTombMaze.getGroupId())){
            this.dbTombMazeMap.put(dbTombMaze.getGroupId(), new ConcurrentHashMap<>());
        }
        this.dbTombMazeMap.get(dbTombMaze.getGroupId()).put(dbTombMaze.getFloor(), dbTombMaze);
    }

    public Map<Integer, Map<Integer, DbTombMaze>> getDbTombMazeMap() {
        return dbTombMazeMap;
    }

    public Map<Integer, DbTombMaze> getDbTombMazeMap(int groupId) {
        return dbTombMazeMap.get(groupId);
    }


    public void generateNpcBaseBlood(int groupId) {
        long currentTimeMillis = System.currentTimeMillis();
        for (DbTombServerData dbTombServerData : this.getDbTombServerDataMap(groupId).values()) {
            if(groupId != dbTombServerData.getGroupId()){
                continue;
            }
            if (!DateHelper.isSameDay(dbTombServerData.getUpdateTime() / 1000, currentTimeMillis / 1000)) {
                return;
            }
        }

        for (ActivityTombEvent activityTombEvent : this.activity.getConfig().getEvents(2)) {
            long min = activityTombEvent.getParam2().get(2).get(0);

            long maxHp1 = this.getHpByRank(groupId, activityTombEvent.getParam2().get(0).get(0), activityTombEvent.getParam2().get(0).get(1));
            long maxHp2 = this.getHpByRatio(groupId, activityTombEvent.getParam2().get(1).get(0), activityTombEvent.getParam2().get(1).get(1));

            long resultHp;
            resultHp = Math.max(min, maxHp1);
            resultHp = Math.max(resultHp, maxHp2);

            Tomb.TombLogCrossToGameReq.Builder builder = Tomb.TombLogCrossToGameReq.newBuilder();
            builder.setLEnum(Tomb.GameTombLogEnum.GENERATE_BLOOD);
            Tomb.TombLogNpcBaseBlood.Builder npcBaseBloodBuilder = Tomb.TombLogNpcBaseBlood.newBuilder();
            npcBaseBloodBuilder.setNpcId(activityTombEvent.getNpcId());
            npcBaseBloodBuilder.setHp1(maxHp1);
            npcBaseBloodBuilder.setHp2(maxHp2);
            npcBaseBloodBuilder.setMinHp(min);
            npcBaseBloodBuilder.setResultHp(resultHp);
            builder.setNpcBaseBlood(npcBaseBloodBuilder);
            List<Long> serverSet = TombServiceMgr.getServerIdSet(activity.getActivityInfo(), groupId);
            for (long serverId : serverSet) {
                MessageHelper.sendToGameServer(serverId, GameProtocol.S_TOMB_LOG_CROSS_TO_GAME, this.activity.getActivityInfo().getActivityId(), 0, builder, null);
            }
        }


    }

}
